#include "Buffer.hpp"

#include <unistd.h>
#include <sys/uio.h>
#include <cassert>
#include <iostream>

Buffer::Buffer(size_t initialSize)
    : buffer_(initialSize),
      readerIndex_(0),
      writerIndex_(0) {}

size_t Buffer::readableBytes() const {
    return writerIndex_ - readerIndex_;
}

size_t Buffer::writableBytes() const {
    return buffer_.size() - writerIndex_;
}

size_t Buffer::prependableBytes() const {
    return readerIndex_;
}

const char* Buffer::peek() const {
    return buffer_.data() + readerIndex_;
}

void Buffer::retrieve(size_t len) {
    assert(len <= readableBytes());
    readerIndex_ += len;
    if (readerIndex_ == writerIndex_) {
        // 全部读完，重置
        readerIndex_ = 0;
        writerIndex_ = 0;
    }
}

void Buffer::retrieveAll() {
    readerIndex_ = writerIndex_ = 0;
}

std::string Buffer::retrieveAllAsString() {
    std::string result(peek(), readableBytes());
    retrieveAll();
    return result;
}

void Buffer::append(const char* data, size_t len) {
    ensureWritableBytes(len);
    std::copy(data, data + len, beginWrite());
    hasWritten(len);
}

void Buffer::append(const std::string& str) {
    append(str.data(), str.size());
}

char* Buffer::beginWrite() {
    return buffer_.data() + writerIndex_;
}

void Buffer::hasWritten(size_t len) {
    writerIndex_ += len;
}

void Buffer::ensureWritableBytes(size_t len) {
    if (writableBytes() < len) {
        makeSpace(len);
    }
}

void Buffer::makeSpace(size_t len) {
    if (writableBytes() + prependableBytes() < len) {
        buffer_.resize(writerIndex_ + len);
    } else {
        // 将可读数据前移
        size_t readable = readableBytes();
        std::copy(buffer_.data() + readerIndex_,
                  buffer_.data() + writerIndex_,
                  buffer_.data());
        readerIndex_ = 0;
        writerIndex_ = readable;
    }
}

char* Buffer::begin() {
    return &*buffer_.begin();
}

const char* Buffer::begin() const {
    return &*buffer_.begin();
}

void Buffer::shrink(size_t reserve) {
    std::vector<char> newBuf;
    newBuf.resize(readableBytes() + reserve);
    std::copy(peek(), peek() + readableBytes(), newBuf.begin());
    buffer_.swap(newBuf);
    writerIndex_ = readableBytes();
    readerIndex_ = 0;
}

ssize_t Buffer::readFd(int fd, int* savedErrno) {
    char extraBuf[65536];
    struct iovec vec[2];

    const size_t writable = writableBytes();
    vec[0].iov_base = beginWrite();
    vec[0].iov_len  = writable;
    vec[1].iov_base = extraBuf;
    vec[1].iov_len  = sizeof(extraBuf);

    const int iovcnt = (writable < sizeof(extraBuf)) ? 2 : 1;
    const ssize_t n = ::readv(fd, vec, iovcnt);
    if (n < 0) {
        *savedErrno = errno;
    } else if (n <= static_cast<ssize_t>(writable)) {
        hasWritten(n);
    } else {
        hasWritten(writable);
        append(extraBuf, n - writable);
    }

    return n;
}
